package com.ruleengine.worker.rulechain;

import static com.ruleengine.worker.rulechain.ChainUtil.buildChain;

import java.util.List;
import java.util.Optional;

import com.ruleengine.db.model.Rule;
import com.ruleengine.db.model.Rulechain;
import com.ruleengine.db.model.RulechainState;
import com.ruleengine.db.model.RulechainStatus;
import com.ruleengine.db.model.Rulerelation;
import com.ruleengine.db.service.RuleService;
import com.ruleengine.db.service.RulechainService;
import com.ruleengine.db.service.RulerelationService;
import com.ruleengine.worker.exception.ChainManagerException;
import com.ruleengine.worker.model.Chain;
import com.ruleengine.worker.model.Status;
import com.ruleengine.worker.runner.chain.ChainRunnerManager;

import lombok.extern.slf4j.Slf4j;

/**
 * @author lianghanwei on 2024/3/15
 */
@Slf4j
public class ChainManagerImpl implements ChainManager {

    private final RulechainService rulechainService;
    private final RuleService ruleService;
    private final RulerelationService rulerelationService;

    private final ChainRunnerManager chainRunnerManager;

    public ChainManagerImpl(ChainRunnerManager chainRunnerManager,
        RulechainService rulechainService,
        RuleService ruleService,
        RulerelationService rulerelationService) {
        this.chainRunnerManager = chainRunnerManager;
        this.rulechainService = rulechainService;
        this.ruleService = ruleService;
        this.rulerelationService = rulerelationService;
    }

    @Override
    public void addChain(Chain chain) {
        chainRunnerManager.add(chain);
    }

    @Override
    public Chain getChain(String chainId) {
        return getChain(chainId, 0);
    }

    @Override
    public Chain getChain(String chainId, int expectedMinVersion) {
        Optional<Rulechain> rulechainOpt = rulechainService.getRulechain(chainId, expectedMinVersion, RulechainState.ENABLED);
        if (rulechainOpt.isEmpty()) {
            log.debug("Enabled rulechain is not exist, rulechainId={}", chainId);
            return null;
        }

        Rulechain rulechain = rulechainOpt.get();
        log.debug("ChainManager load rulechain, rulechain={}", rulechain);

        String tenantId = rulechain.getTenantId();

        // get related rules
        List<Rule> rules = ruleService.getRules(tenantId, chainId);
        log.debug("ChainManager load rules, rules={}", rules);

        List<Rulerelation> rulerelations = rulerelationService.getRulerelations(tenantId, chainId);
        log.debug("ChainManager loaded relations, relations={}", rulerelations);

        // convert Rulechain to Chain
        return buildChain(rulechain, rules, rulerelations);
    }

    @Override
    public void deleteChain(String chainUid) {
        chainRunnerManager.remove(chainUid);
    }

    @Override
    public void updateChain(String chainId, Chain chain) {
        chainRunnerManager.update(chainId, chain);
    }

    @Override
    public void updateStatus(String chainId, int expectedVersion, Status status) {
        RulechainStatus rulechainStatus = RulechainStatus.valueOf(status.getPhase().name());

        boolean isUpdated = rulechainService.updateRulechainStatus(chainId, expectedVersion, rulechainStatus);

        if (!isUpdated) {
            log.warn("ChainManager update rulechain status failed, chainId={}, status={}", chainId, rulechainStatus);
            throw new ChainManagerException("ChainManager update rulechain status failed.");
        }
    }
}
